﻿namespace Jsl.FxCop
{
    using System;
    using System.Diagnostics;
    using System.Diagnostics.Contracts;
    using Microsoft.FxCop.Sdk;
    using Microsoft.VisualStudio.CodeAnalysis.Extensibility;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    /// <summary>
    /// Utility assertions that simplify checking FxCop rule results.
    /// </summary>
    public static class ProblemAssert
    {
        #region Public Methods
        /// <summary>
        /// Asserts that the problems has the expected values.
        /// </summary>
        /// <param name="problem">The problem.</param>
        /// <param name="certainty">The certainty.</param>
        /// <param name="fixCategory">The fix category.</param>
        /// <param name="messageLevel">The message level.</param>
        /// <param name="resolution">The resolution.</param>
        [DebuggerHidden]
        public static void AssertMatches(
            this Problem problem,
            int certainty,
            FixCategories fixCategory,
            MessageLevel messageLevel,
            string resolution)
        {
            problem.AssertMatches(
                certainty,
                fixCategory,
                messageLevel,
                actualResolution => VerifyResolution(resolution, actualResolution));
        }

        /// <summary>
        /// Asserts that the problems has the expected values.
        /// </summary>
        /// <param name="problem">The problem.</param>
        /// <param name="certainty">The certainty.</param>
        /// <param name="fixCategory">The fix category.</param>
        /// <param name="messageLevel">The message level.</param>
        /// <param name="resolution">The resolution.</param>
        [DebuggerHidden]
        public static void AssertMatches(
            this Problem problem,
            int certainty,
            FixCategories fixCategory,
            MessageLevel messageLevel,
            Action<string> verifyResolution)
        {
            if (problem == null)
            {
                throw new ArgumentNullException("problem");
            }

            Assert.AreEqual(certainty, problem.Certainty, "The Certainty value does not match.");
            Assert.AreEqual(fixCategory, problem.FixCategory, "The FixCategory does not match.");
            Assert.AreEqual(messageLevel, problem.MessageLevel, "The MessageLevel does not match.");
            verifyResolution(problem.Resolution.ToString());
        }

        /// <summary>
        /// Asserts that there are no problems.
        /// </summary>
        /// <param name="problems">The problems to check.</param>
        /// <returns>The problems.</returns>
        [DebuggerHidden]
        public static ProblemCollection HasNoMoreProblems(this ProblemCollection problems)
        {
            if (problems == null)
            {
                throw new ArgumentNullException("problems");
            }

            if (problems.Count > 0)
            {
                Assert.Fail(
                    "There are {0} more problems. The first has a resolution of '{1}'.",
                    problems.Count,
                    problems[0].Resolution);
            }

            return problems;
        }

        /// <summary>
        /// Asserts that there is only one problem and that it has the expected values.
        /// </summary>
        /// <param name="problems">The problems to check.</param>
        /// <param name="certainty">The certainty.</param>
        /// <param name="fixCategory">The fix category.</param>
        /// <param name="messageLevel">The message level.</param>
        /// <param name="resolution">The resolution.</param>
        /// <returns>The problems.</returns>
        [DebuggerHidden]
        public static ProblemCollection HasProblem(
            this ProblemCollection problems,
            int certainty,
            FixCategories fixCategory,
            MessageLevel messageLevel,
            string resolution)
        {
            return problems.HasProblem(
                certainty,
                fixCategory,
                messageLevel,
                actualResolution => VerifyResolution(resolution, actualResolution));
        }

        /// <summary>
        /// Asserts that there is only one problem and that it has the expected values.
        /// </summary>
        /// <param name="problems">The problems to check.</param>
        /// <param name="certainty">The certainty.</param>
        /// <param name="fixCategory">The fix category.</param>
        /// <param name="messageLevel">The message level.</param>
        /// <param name="resolution">The resolution.</param>
        /// <returns>The problems.</returns>
        [DebuggerHidden]
        public static ProblemCollection HasProblem(
            this ProblemCollection problems,
            int certainty,
            FixCategories fixCategory,
            MessageLevel messageLevel,
            Action<string> verifyResolution)
        {
            if (problems == null)
            {
                throw new ArgumentNullException("problems");
            }

            Contract.EndContractBlock();

            if (problems.Count == 0)
            {
                throw new AssertFailedException("There are no more problems.");
            }

            // Verify and delete the problem.
            var problem = problems[0];
            problem.AssertMatches(certainty, fixCategory, messageLevel, verifyResolution);
            problems.Remove(problem);

            return problems;
        }
        #endregion Public Methods

        #region Private Methods
        private static void VerifyResolution(string expectedResolution, string actualResolution)
        {
            Assert.AreEqual(expectedResolution, actualResolution, "The resolutions do not match.");
        }
        #endregion Private Methods
    }
}